/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.openbookformat.project;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Properties;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.openide.filesystems.FileChangeAdapter;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 *
 * @author MUFEED
 */
public class BookProp implements Lookup.Provider, PropertyChangeListener {
    
    String title;
    String version;
    String[] author;
    String series;
    ArrayList<Chapter> chapters;
    ArrayList<HeadingPointer> headings;

    OBFProject project;
    Lookup lookup;
    MyObservable observable;


    public BookProp(OBFProject project) {
        this.project = project;
        observable = new MyObservable();
        lookup = Lookups.fixed(observable);
        chapters = new ArrayList<Chapter>();
        headings = new ArrayList<HeadingPointer>();
        System.out.println(project.getProjectDirectory().getFileObject("src").getPath());
        
        project.getProjectDirectory().getFileObject("src").
                addRecursiveListener(new FileChangeAdapter() {

            @Override
            public void fileFolderCreated(FileEvent fe) {

                System.out.println("FOlder CHanged");
                sourceFolderContentsChanged();
            }

        });
        init();
    }

    public String[] getAuthor() {
        return author;
    }

    public void setAuthor(String[] author) {
        this.author = author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getVersion() {
        return version;
    }

    public int getTotalNumberOfChapter() {
        return chapters.size();
    }

    public void addChapter(Chapter chpt) {
        chapters.add(chpt);
        observable.arrayChanged(chapters);
    }

    public void deleteChapter(Chapter chpt) {
        chapters.remove(chpt);
        observable.arrayChanged(chapters);
    }

    public int moveChapterUp(Chapter chpt) {
        return moveObjectUp(chpt, chapters);
    }
    
    public int moveChapterDown(Chapter chpt) {
        return moveObjectDown(chpt, chapters);
    }

    public Chapter getChapter(int idx) {
        return chapters.get(idx);
    }

    public int indexOf(Chapter chpt) {
        return chapters.indexOf(chpt);
    }

    public List getChapters() {
        return chapters;
    }
    
    private int moveObjectUp(Object obj, ArrayList array) {
        int idx = array.indexOf(obj);
        if (idx == -1)
            return idx;

        if(idx > 0) {
            Object upperObj = array.get(idx - 1);
            array.set(idx, upperObj);
            array.set(idx - 1, obj);
            observable.arrayChanged(array);
            return idx -1;
        }
        return idx;
    }

    private int moveObjectDown(Object obj, ArrayList array) {
        int idx = array.indexOf(obj);
        if (idx == -1)
            return idx;

        if(idx < array.size() - 1) {
            Object downObj = array.get(idx + 1);
            array.set(idx, downObj);
            array.set(idx + 1, obj);
            observable.arrayChanged(array);
            return idx + 1;
        }
        return idx;
    }

    public int getTotalNumberOfHeadings() {
        return headings.size();
    }

    public void insertHeading(int index, HeadingPointer pointer) {
        headings.add(index, pointer);
        pointer.addPropertyChangeListener(this);
        observable.arrayChanged(headings);
    }
    
    public void addHeading(HeadingPointer pointer) {
        insertHeading(headings.size(), pointer);
    }

    public void deleteHeading(HeadingPointer pointer) {
        pointer.removePropertyChangeListener(this);
        headings.remove(pointer);
        observable.arrayChanged(headings);
    }
    
    private void refreshHeadings() {
        observable.arrayChanged(headings);
    }
     
    public int moveHeadingUp(HeadingPointer pointer) {
        return moveObjectUp(pointer, headings);
    }
    
    public int moveHeadingDown(HeadingPointer pointer) {
        return moveObjectDown(pointer, headings);
    }
    
    public HeadingPointer getHeadingPointer(int idx) {
        return headings.get(idx);
    }

    public int indexOf(HeadingPointer pointer) {
        return headings.indexOf(pointer);
    }

    public List getHeadings() {
        return headings;
    }
    
    public String getSeries() {
        return series;
    }

    public void setSeries(String series) {
        this.series = series;
    }

    private void init() {
        InputStream in = null;
        try {
            //Project changed
            //Read porject pro files
            //System.out.println("Reading Project File ...");
            in = project.getProjectDirectory().getFileObject(
                    OBFProjectFactory.PROJECT_FILE).getInputStream();
            //System.out.println("Done Reading Project File.");
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
            return;
        }

        SAXParserFactory parserFactory = SAXParserFactory.newInstance();
        SAXParser parser;
        try {
            parser = parserFactory.newSAXParser();
            parser.parse(in, new DefaultHandler() {
                private String[] nodeNames = {"chapter"};
                private int nodeIdx = -1;
                private String chars = null;

                @Override
                public void startElement(String uri, String localName, String qName, org.xml.sax.Attributes atrbts) throws SAXException {
                    //super.startElement(string, string1, string2, atrbts);
                    nodeIdx = -1;
                    for (int i = 0; i < nodeNames.length; i++) {
                        if(qName.equalsIgnoreCase(nodeNames[i])) {
                            nodeIdx = i;
                            break;
                        }
                    }
                    //System.out.println("StartElement:" + nodeIdx);
                }

                @Override
                public void characters(char[] ch, int start, int length) throws SAXException {
                    chars = null;
                    if(nodeIdx != -1)
                        chars = new String(ch, start, length);

                    //System.out.println("characters:" + chars);
                }

                @Override
                public void endElement(String string, String string1, String string2) throws SAXException {
                    switch (nodeIdx) {
                        case 0:
                            //chapter
                            chapters.add(new Chapter(chars));
                            break;
                        default:
                    }
                    nodeIdx = -1;

                    //System.out.println("EndElement:" + nodeIdx);
                }

            });
        } catch (ParserConfigurationException ex) {
            Exceptions.printStackTrace(ex);
            return;
        } catch (SAXException ex) {
            Exceptions.printStackTrace(ex);
            return;
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
        sourceFolderContentsChanged();
        
        //Reading Heading File
        Properties hd = new Properties();
        try {
            System.out.println("Opening:" + project.getProjectDirectory().getFileObject(
                    OBFProjectFactory.HEADING_FILE).getInputStream() );
            FileInputStream fis = new FileInputStream(
                    project.getProjectDirectory().getFileObject(
                    OBFProjectFactory.HEADING_FILE).getPath());
            hd.load(fis);
            
            int count = Integer.parseInt(hd.getProperty("heading_count"));
            int chIdx = -1;
            String ptrIdx = null;
            for(int i = 0; i < count; i++) {
                ptrIdx = (i < 100?"0":"") + (i < 10?"0":"") + i;
                System.out.println("PtrIdx:" + ptrIdx);
                chIdx = indexOf(new Chapter(hd.getProperty("idx_" + ptrIdx +"_chapter").trim()));
                addHeading(new HeadingPointer(
                        chIdx == -1?null:getChapter(chIdx),
                        Integer.parseInt(hd.getProperty("idx_" + ptrIdx +"_offset").trim()), 
                        Integer.parseInt(hd.getProperty("idx_" + ptrIdx +"_level").trim()),
                        hd.getProperty("idx_" + ptrIdx +"_heading").trim()));
                System.out.println(headings.get(headings.size() -1));
            }
            fis.close();
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
        
    }

    private void sourceFolderContentsChanged() {
        FileObject[] projFiles = project.getProjectDirectory().getFileObject("src").getChildren();

        //check files in the xml file
        boolean found = false;
        for(int i = getTotalNumberOfChapter() -1 ; i > -1; i--) {
            found = false;
            for (int j = 0; j < projFiles.length; j++) {
                if(chapters.contains(new Chapter(projFiles[j].getName()))) {
                    //file exist --> keep
                    found = true;
                    break;
                }
            }
            if(!found)
                chapters.remove(i);
        }

        //add nonexisting files to the xml files
        for(int i = 0 ; i < projFiles.length; i++) {
            if(!chapters.contains(new Chapter(projFiles[i].getName())))
                chapters.add(new Chapter(projFiles[i].getName()));
        }
    }

    @Override
    public Lookup getLookup() {
        return lookup;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if(evt.getPropertyName().equals(HeadingPointer.PROP_HEADING) || 
                evt.getPropertyName().equals(HeadingPointer.PROP_LEVEL)) {
            refreshHeadings();
        }
    }

    class MyObservable extends Observable {

        void arrayChanged(ArrayList array) {
            super.setChanged();
            super.notifyObservers(array);
        }
    }

    public OBFProject getProject() {
        return project;
    }

}